﻿/*
 * This file is part of CommunitySettlers.
 *
 * CommunitySettlers is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * CommunitySettlers is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with CommunitySettlers.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Author: Christoph Husse
 * 
 * Also checkout our homepage: http://communitysettlers.codeplex.com/
 */
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;


namespace SettlersEngine
{
    internal class NativeThreadProcessor<TExchangeType> : DependencyObject
    {
        private Thread m_NativeThread;
        private System.Windows.Threading.DispatcherTimer m_WPFTimer = new System.Windows.Threading.DispatcherTimer();
        private TExchangeType m_VolatileResult = default(TExchangeType);
        private TExchangeType m_LastResult;
        private TExchangeType m_BufferedResult;
        private Exception m_Exception = null;
        private Object m_Lock = new Object();
        private Boolean m_IsDisposed = false;

        private System.Diagnostics.Stopwatch m_Watch = new System.Diagnostics.Stopwatch();
        private UniqueMap<Int64, TExchangeType> m_NativeQueue = new UniqueMap<Int64, TExchangeType>();
        private UniqueMap<Int64, TExchangeType> m_WPFQueue = new UniqueMap<Int64, TExchangeType>();

        public ManualResetEventSlim Notification { get; private set; }
        public TimeSpan WPFInterval { get { return m_WPFTimer.Interval; } set { m_WPFTimer.Interval = value; } }
        public TimeSpan NativeInterval { get; set; }

        public NativeThreadProcessor()
        {
            Notification = new ManualResetEventSlim(false);
            m_Watch.Start();

            m_NativeThread = new Thread(m_NativeThread_Execute);
            m_NativeThread.IsBackground = true;
            m_NativeThread.Start();

            m_WPFTimer.Interval = TimeSpan.FromMilliseconds(0);
            m_WPFTimer.Tick += new EventHandler(m_WPFTimer_Tick);
        }

        protected virtual void OnWPFError(Exception inException)
        {
        }

        protected virtual void OnWPFUpdate(TExchangeType inResult)
        {
        }

        protected virtual Boolean OnNativeProcess(ref TExchangeType refVolatileResult, out TExchangeType outStableResult)
        {
            outStableResult = default(TExchangeType);

            return false;
        }

        ~NativeThreadProcessor()
        {
            Dispose();
        }

        public void Dispose()
        {
            if(m_IsDisposed)
                return;

            try
            {
                m_IsDisposed = true;
                NativeInterval = TimeSpan.FromMilliseconds(0);
                Notification.Set();
                m_NativeThread.Join(1000);
                Notification.Dispose();
            }
            catch
            {
                m_NativeThread.Abort();
                m_WPFTimer.Stop();
            }
            finally
            {
                m_IsDisposed = true;
                m_WPFTimer = null;
                m_NativeThread = null;
                m_VolatileResult = default(TExchangeType);
                m_LastResult = default(TExchangeType);
                m_BufferedResult = default(TExchangeType);
                m_Exception = null;
            }
        }

        private void m_WPFTimer_Tick(object sender, EventArgs e)
        {
            lock (m_Lock)
            {
                m_WPFTimer.Stop();
            }

            // check for exception
            Exception except;

            lock (m_Lock)
            {
                except = m_Exception;

                m_Exception = null;
            }

            if (except != null)
            {
                OnWPFError(except);

                return;
            }

            // process results
            TExchangeType current;

            lock (m_Lock)
            {
                current = m_VolatileResult;

                if (current == null)
                    current = m_BufferedResult;

                if (m_LastResult != null)
                {
                    if (m_LastResult.Equals(current))
                        return;
                }
                else if (current == null)
                    return;

                m_LastResult = current;
            }

            m_WPFQueue.Add(m_Watch.ElapsedMilliseconds, m_LastResult);

            OnWPFUpdate(m_LastResult);
        }

        /////// ATTENTION: The following is a NET thread! DON'T call any WPF methods/properties...
        private void m_NativeThread_Execute(object unused)
        {
            try
            {
                while (!m_IsDisposed)
                {
                    Notification.Wait(NativeInterval);
                    Notification.Reset();

                    if (m_IsDisposed)
                        break;

                    try
                    {
                        TExchangeType tmp;

                        if (OnNativeProcess(ref m_VolatileResult, out tmp))
                        {
                            lock (m_Lock)
                            {
                                m_BufferedResult = tmp;
                                m_VolatileResult = default(TExchangeType);
                                m_WPFTimer.Start();
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        lock (m_Lock)
                        {
                            m_Exception = e;
                            m_WPFTimer.Start();
                        }
                    }
                }
            }
            catch (ThreadAbortException)
            {
            }
        }
    }
}
